using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Dogfight2008.Common;
using OpenTK;
using OpenTK.OpenGL;
using OpenTK.Math;
using OpenTK.OpenGL.Enums;
using OpenTK.Fonts;
using System.Diagnostics;

namespace Dogfight2008
{
  public partial class MainForm : Form
  {
    Game game;
    DogfightStateMachine dogfight;

    public MainForm()
    {
      InitializeComponent();

      game = new Game();

      dogfight = new DogfightStateMachine(game);
    }

    Stopwatch stopwatch = new Stopwatch();
    double prevMs = 0;
    int currWholeSecond = 0;
    int idleCount = 0;
    int paintCount = 0;

    void Application_Idle(object sender, EventArgs e)
    {
      double totalMs = stopwatch.Elapsed.TotalMilliseconds;
      double deltaMS = totalMs - prevMs;
      if (deltaMS > 250)
        deltaMS = 0;
      prevMs = totalMs;
      int second = (int)(totalMs/1000.0);
      if (second != currWholeSecond)
      {
        currWholeSecond = second;
        label1.Text = "IdlePS=" + idleCount.ToString() + "  PaintPS=" + paintCount.ToString();
        idleCount = 0;
        paintCount = 0;
      }
      dogfight.Tick(deltaMS);
      glControl.Invalidate();
      idleCount++;
    }

    private float viewdistance = 10000;
    private void MainForm_Load(object sender, EventArgs e)
    {
      GLControlUtil.Init(glControl, viewdistance);
      GLUtil.SunLightDirection(0, 1, 0);
      stopwatch.Start();
      Application.Idle += Application_Idle;
      //this.Text = GL.GetString(StringName.Renderer);
    }

    private void MainForm_Resize(object sender, EventArgs e)
    {
      GLControlUtil.HandleResize(glControl, viewdistance);
    }

    private void glControl_Resize(object sender, EventArgs e)
    {
      GLControlUtil.HandleResize(glControl, viewdistance);
    }

    class Renderer : IRenderer
    {
      GLControl ctrl;

      public Renderer(GLControl ctrl)
      {
        this.ctrl = ctrl;
      }

      #region IRenderer Members

      Dictionary<string, TextHandle> cachedHandles = new Dictionary<string, TextHandle>();
      TextureFont[] fonts = new TextureFont[]{
        new TextureFont(new Font(FontFamily.GenericMonospace, 12.0f)),
        new TextureFont(new Font(FontFamily.GenericSerif, 16.0f)),
        new TextureFont(new Font(FontFamily.GenericSansSerif, 24.0f)),
        new TextureFont(new Font(FontFamily.GenericSansSerif, 48.0f)),
      };
      TextPrinter printer = new TextPrinter();

      public void TextAt(string text, Uniform x, Uniform y, Uniform height, Anchor anchor)
      {
        GL.MatrixMode(MatrixMode.Projection);
        GL.PushMatrix();
        GL.MatrixMode(MatrixMode.Modelview);

        GL.PushAttrib(AttribMask.AllAttribBits);
        GL.PushMatrix();
        GL.PushClientAttrib(ClientAttribMask.ClientAllAttribBits);
        GL.Disable(EnableCap.DepthTest);
        GL.DepthMask(false);

        GL.Disable(EnableCap.Lighting);

        int fontIndex = GetTextSizeIndex(height);
        TextureFont font = fonts[fontIndex];
        string textName = text + "_" + fontIndex.ToString();
        TextHandle handle = GetTextHandle(text, font);
        printer.Begin();
        GL.Translate((float)(x.Value * ctrl.Width), (float)(y.Value * ctrl.Height), 0);
        if (anchor == Dogfight2008.Anchor.Center)
        {
          float txWidth, txHeight;
          font.MeasureString(text, out txWidth, out txHeight);
          GL.Translate(-txWidth / 2, -txHeight / 2, 0);
        }
        printer.Draw(handle); 
        printer.End();

        GL.Enable(EnableCap.Lighting);

        GL.DepthMask(true);
        GL.Enable(EnableCap.DepthTest);
        GL.PopClientAttrib();
        GL.PopMatrix();
        GL.PopAttrib();

        GL.MatrixMode(MatrixMode.Projection);
        GL.PopMatrix();
        GL.MatrixMode(MatrixMode.Modelview);
      }

      private static int GetTextSizeIndex(Uniform height)
      {
        if (height.Value < 0.1)
          return 0;
        if (height.Value < 0.2)
          return 1;
        if (height.Value < 0.4)
          return 2;
        return 3;
      }

      private TextHandle GetTextHandle(string text, TextureFont font)
      {
        if (!cachedHandles.ContainsKey(text))
        {
          TextHandle handle;
          printer.Prepare(text, font, out handle);
          cachedHandles[text] = handle;
        }
        return cachedHandles[text];
      }

      ColoredPolygonMesh GetMesh(Mesh mesh)
      {
        if (!meshCache.ContainsKey(mesh))
          meshCache[mesh] = CplLoader.FromByteArray(FindMesh(mesh)); // TODO: way to unify enum/resx name?
        return meshCache[mesh];
      }

      private byte[] FindMesh(Mesh mesh)
      {
        switch (mesh)
        {
          case Mesh.RedPlane: return CPL.RedPlane;
          case Mesh.YellowPlane: return CPL.YellowPlane;
          case Mesh.Backdrop: return CPL.backdrop;
          case Mesh.Shot: return CPL.shot;
        }
        return CPL.RedPlane;
      }

      Dictionary<Mesh, ColoredPolygonMesh> meshCache = new Dictionary<Mesh, ColoredPolygonMesh>();
      public void DrawMesh(Mesh mesh)
      {
        GLUtil.DrawMesh(GetMesh(mesh));
      }

      public void DrawMeshIgnoreColors(Mesh mesh)
      {
        GLUtil.DrawMeshIgnoreColor(GetMesh(mesh));
      }

      public void Rotate(Vector3 axis, double rad)
      {
        float angle = (float)(Calc.RadiansToDegrees((float)rad));
        GL.Rotate(angle, axis);
      }

      public void Translate(Vector3 translation)
      {
        GL.Translate(translation);
      }

      public void DrawLine(Vector3 from, Vector3 to)
      {
        GL.Disable(EnableCap.Lighting);
        GL.Begin(BeginMode.Lines);
        GL.Vertex3(from);
        GL.Vertex3(to);
        GL.End();
        GL.Enable(EnableCap.Lighting);
      }

      public void Color(Color color)
      {
        GL.Color3(color);
      }

      public void PushMatrix()
      {
        GL.PushMatrix();
      }

      public void PopMatrix()
      {
        GL.PopMatrix();
      }

      #endregion
    }

    Renderer renderer = null;

    //bool white = false;
    void glControl_Paint(object sender, PaintEventArgs e)
    {
      if (renderer == null)
        renderer = new Renderer(glControl);

      //if (white)
      //  GL.ClearColor(Color.White);
      //else
      //  GL.ClearColor(Color.Black);
      //white = !white;

      Stopwatch sw = new Stopwatch();
      sw.Start();
      GLUtil.Clear();
      GL.Finish();
      sw.Stop();
      double elapsed1 = sw.Elapsed.TotalMilliseconds;
      GL.Translate(0, 100, -10);

      dogfight.Render(renderer);
      paintCount++;
      Stopwatch sw2 = new Stopwatch();
      sw2.Start();
      GLControlUtil.SwapBuffers(glControl);
      GL.Finish();
      double elapsed2 = sw2.Elapsed.TotalMilliseconds;

      //label2.Text = elapsed1.ToString() + " " + elapsed2.ToString();
    }

    private void glControl_KeyDown(object sender, KeyEventArgs e)
    {
      dogfight.KeyDown(e.KeyCode);
    }

    private void glControl_KeyUp(object sender, KeyEventArgs e)
    {
      dogfight.KeyUp(e.KeyCode);
    }

    private void tweakToolStripMenuItem_Click(object sender, EventArgs e)
    {
      Application.Idle -= Application_Idle;
      TweakConfig config = new TweakConfig();
      dogfight.TweakConfig(config);
      using (TweakDialog dlg = new TweakDialog(config))
        dlg.ShowDialog();
      dogfight.TweakConfig(config);
      Application.Idle += Application_Idle;
    }

  }
}